/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.statemachine.editpart;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.gef.DragTracker;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ElementImport;
import org.eclipse.uml2.uml.Pseudostate;
import org.eclipse.uml2.uml.PseudostateKind;

import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.editpart.DiagramEditPart;
import com.cea.papyrus.core.editpart.GraphNodeWithSemanticEditPart;
import com.cea.papyrus.core.editpart.LinkNameDisplayEditPart;
import com.cea.papyrus.core.editpart.connection.AnchorMngr;
import com.cea.papyrus.core.editpart.connection.EllipseAnchorMngr;
import com.cea.papyrus.core.figures.ElementFigure;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.diagram.statemachine.figure.PseudostateChoiceFigure;
import com.cea.papyrus.diagram.statemachine.figure.PseudostateDeepHistoryFigure;
import com.cea.papyrus.diagram.statemachine.figure.PseudostateEntryPointFigure;
import com.cea.papyrus.diagram.statemachine.figure.PseudostateExitPointFigure;
import com.cea.papyrus.diagram.statemachine.figure.PseudostateForkFigure;
import com.cea.papyrus.diagram.statemachine.figure.PseudostateInitialFigure;
import com.cea.papyrus.diagram.statemachine.figure.PseudostateJoinFigure;
import com.cea.papyrus.diagram.statemachine.figure.PseudostateJunctionFigure;
import com.cea.papyrus.diagram.statemachine.figure.PseudostateShallowHistoryFigure;
import com.cea.papyrus.diagram.statemachine.figure.PseudostateTerminateFigure;
import com.cea.papyrus.diagram.statemachine.helper.NodePseudostateHelper;
import com.cea.papyrus.diagraminterchange2.di2.GraphConnector;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Property;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * Controler for Pseudostate in state machine diagrams.
 * 
 * @author David Servat
 * @since 29 jan. 07
 */
public class PseudostateEditPart extends GraphNodeWithSemanticEditPart{

	/** associated name editpart */
	protected  LinkNameDisplayEditPart nameEditpart;

	/**
	 * The channel.
	 */
	private int channel = 501;

	/**
	 * The constructor.
	 * 
	 * @param modelManager the associated ModelManager
	 * @param graphNode the GraphNode to be managed
	 */
	public PseudostateEditPart(GraphNode graphNode, ModelManager modelManager){
		super(graphNode, modelManager);

		Debug.debug(this,"call PseudoStateEditPart(GraphNode graphnode, ModelManager modelManager)",channel);
	}

	/**
	 * Calls for the appropriate figure associated to the element.
	 * 
	 * @return 
	 * 
	 * @see com.cea.papyrus.common.editpart.GraphNodeWithSemanticEditPart#createFigure()
	 */
	@Override
	protected ElementFigure getNodeFigure(){
		return (ElementFigure)createFigure() ;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#getDragTracker(org.eclipse.gef.Request)
	 */
	@Override
	public DragTracker getDragTracker(Request request) {
		return new PseudostateDragTracker(this);
	}


	/**
	 * Direct creation of a figure here based on the associated icon of the UML element.
	 * 
	 * @return 
	 * 
	 * @see com.cea.papyrus.common.editpart.GraphNodeWithSemanticEditPart#createFigure()
	 */
	@Override
	protected IFigure createFigure(){
		PseudostateKind kind = ((Pseudostate)getUmlElement()).getKind();
		switch(kind.getValue()){
		case PseudostateKind.CHOICE:
			return new PseudostateChoiceFigure();
		case PseudostateKind.DEEP_HISTORY:
			return new PseudostateDeepHistoryFigure();
		case PseudostateKind.ENTRY_POINT:
			return new PseudostateEntryPointFigure();
		case PseudostateKind.EXIT_POINT:
			return new PseudostateExitPointFigure();
		case PseudostateKind.FORK:
			return new PseudostateForkFigure();
		case PseudostateKind.INITIAL:
			return new PseudostateInitialFigure();
		case PseudostateKind.JOIN:
			return new PseudostateJoinFigure();
		case PseudostateKind.JUNCTION:
			return new PseudostateJunctionFigure();
		case PseudostateKind.SHALLOW_HISTORY:
			return new PseudostateShallowHistoryFigure();
		case PseudostateKind.TERMINATE:
			return new PseudostateTerminateFigure();
		default:
			return new ElementFigure();

		}
	}

	/**
	 * This method is called whenever graphical changes need to be performed.
	 * 
	 * @see org.eclipse.gef.editparts.AbstractEditPart#refreshVisuals()
	 */
	@Override
	protected void refreshVisuals() {
		Dimension size = getGraphNode().getSize();
		Point p = getGraphNode().getPosition();
		PseudostateKind kind = ((Pseudostate)getUmlElement()).getKind();
		//special sizes for some Pseudostates
		//a little smaller for Initial
		if(size.equals(16, 16)){
			if(kind.equals(PseudostateKind.INITIAL_LITERAL))
				getGraphNode().setSize(getGraphNode().getSize().shrink(2, 2));
			else if(kind.equals(PseudostateKind.JOIN_LITERAL) || kind.equals(PseudostateKind.FORK_LITERAL)){
				getGraphNode().setSize(new Dimension(34, 4));
			}
		}
		size = getGraphNode().getSize();
		if(kind.equals(PseudostateKind.JOIN_LITERAL) || kind.equals(PseudostateKind.FORK_LITERAL)){
			Property prop = getGraphNode().getProperty(NodePseudostateHelper.PSEUDOSTATE_DISPLAY);
			if(prop != null){
				if(prop.getValue().equals(NodePseudostateHelper.PSEUDOSTATE_DISPLAY_HORIZONTAL)){
					if(size.height > size.width){
						size.transpose();
						Iterator iter = getGraphNode().getAnchorage().iterator();
						while(iter.hasNext()){
							GraphConnector connector = (GraphConnector)iter.next();
							Point connectorPosition = connector.getPosition();
							connectorPosition.x = p.x + connectorPosition.y - p.y;
							connectorPosition.y = p.y;
							Debug.debug(this, "connector pos : "+connectorPosition.x+" "+connectorPosition.y, channel);
						}
						Point labelPos = ((GraphNode)getGraphNode().getSemanticChild().get(0)).getPosition();
						labelPos.x = p.x;
						labelPos.y = p.y+size.height+5;
					}
				}
				else if(prop.getValue().equals(NodePseudostateHelper.PSEUDOSTATE_DISPLAY_VERTICAL)){
					if(size.width > size.height){
						size.transpose();
						Iterator iter = getGraphNode().getAnchorage().iterator();
						while(iter.hasNext()){
							GraphConnector connector = (GraphConnector)iter.next();
							Point connectorPosition = connector.getPosition();
							connectorPosition.y = p.y + connectorPosition.x - p.x;
							connectorPosition.x = p.x;
							Debug.debug(this, "connector pos : "+connectorPosition.x+" "+connectorPosition.y, channel);
						}
						Point labelPos = ((GraphNode)getGraphNode().getSemanticChild().get(0)).getPosition();
						labelPos.x = p.x+size.width+5;
						labelPos.y = p.y;
					}
				}
			}
		}

		Rectangle r = new Rectangle(p, size);
		IFigure fig = getFigure();
		((GraphicalEditPart) getParent()).setLayoutConstraint(this, fig, r);
	}

	/**
	 * Creates the anchor manager.
	 * 
	 * @return the anchor mngr
	 */
	@Override
	protected AnchorMngr createAnchorManager() {
		return new EllipseAnchorMngr(this);
	}

	/**
	 * Returns the associated UML element.
	 * 
	 * @return 
	 * 
	 * @see com.cea.papyrus.common.editpart.GraphNodeWithSemanticEditPart#getUmlElement()
	 */
	@Override
	public Pseudostate getUmlElement(){
		Element e = ((Uml1SemanticModelBridge)getGraphNode().getSemanticModel()).getElement();
		// Case of element import
		if(e instanceof ElementImport)
			return (Pseudostate)((ElementImport)e).getImportedElement();
		return (Pseudostate)e;
	}

	/**
	 * Calculates the good position of a entry/exit point by taking account his parent.
	 * @param point the position of the current entry/exit point in absolute coordinate
	 * @return the new position of the entry/exit point.
	 */
	public Point inRunnerOfClass(Point point){
		Dimension connectionPointDimension = getGraphNode().getSize();
		//apply good position , the default is the same point
		//FIXME should be a nodehelper called from the plugin
		com.cea.papyrus.diagram.statemachine.helper.NodePointHelper np = new com.cea.papyrus.diagram.statemachine.helper.NodePointHelper(getUmlElement().getKind());
		return np.inRunner((GraphNode)getGraphNode().getSemanticParent(), point, connectionPointDimension);
	}

	/**
	 * Is true if managed element is Entry or Exit point
	 * @return
	 */
	public boolean isEntryOrExit(){
		PseudostateKind kind = getUmlElement().getKind();
		
		return (kind.equals(PseudostateKind.ENTRY_POINT_LITERAL) | kind.equals(PseudostateKind.EXIT_POINT_LITERAL)); 
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractEditPart#getModelChildren()
	 */
	@Override
	protected List getModelChildren() { //look for all graphnodes, 
		ArrayList contained= new ArrayList();//that is associated to label
	
		// 	contained.add(getGraphNode().getContained().get(0));
		return contained;
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractEditPart#addChild(org.eclipse.gef.EditPart, int)
	 */
	@Override
	protected void addChild(EditPart child, int index) {
		if (child.equals(nameEditpart)){
			((DiagramEditPart)getParent()).addExplicitEditpart(nameEditpart, index);
		}
		else {
			super.addChild(child, index);
		}
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.editpart.GraphNodeWithSemanticEditPart#activate()
	 */
	@Override
	public void activate() {
		if (isActive()) {
			Debug.debug(this, "should not activate an already active edit part", channel);
			return;
		}
		// start listening for changes in the model
		((Notifier)getGraphNode()).eAdapters().add(_adapter);
		super.activate();
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.editpart.GraphNodeWithSemanticEditPart#deactivate()
	 */
	@Override
	public void deactivate() {
		if (!isActive()) {
			Debug.debug(this, "should not deactivate an already deactived edit part", channel);
			return;
		}
		// stop listening for changes in the model
		((Notifier)getGraphNode()).eAdapters().remove(_adapter);
		super.deactivate();
	}
	
    /**
     * Return false.
     * This method is used in {@link com.cea.papyrus.common.editpart.LayoutEditPolicy} in order to
     * know how to compute the coordinate of a added part.
     * @TODO move this method to more appropriate place. 
     * @return
     */
    @Override
    public boolean needRelativeTranslationForAddCommand()
    {
        return isEntryOrExit();
    }
	
	/**
	 * {@inheritDoc} 
	 */
	@Override
	protected boolean checkDefaultEdition() {
		return false;
	}
}
